Intel C++ 无法将 `T **` 转换为 `T const * const *`,GCC 可以
Intel C++ cannot convert `T **` to `T const * const *`, GCC can
问题
现有代码的扩展
我有一个数字库,它的设计考虑了一种“风味”。
现在我想概括一下。基本数据结构是一个“旋量”,它
本身是一个多维矩阵。有很多功能需要
这些旋量的数组。广义函数需要采用一个这样的旋量
每种口味的数组。
假设有一个函数,它至少执行以下操作:
void copy_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}
我现在的概括是这样的:
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}
在真正的代码中,有一个遍历所有num_flav
的循环,但那不是真的
此处演示需要。
据我了解,必须将此解读为 const Spinor *(in[num_flav])
,
所以 in
是指向可能 num_flav
元素(或另一个
数量,因为 foo[]
只是函数参数中的 *foo
)类型
指向常量旋量的指针。
问题是使用 Spinor *non_const[2]
时无法编译
(没有 const
),请参阅 我之前的
问题。从那里的答案我
已经了解到这不能编译,因为在函数内
copy_spinor
,指针non_const[0]
可以指向一些
const
数组 Spinor *
。然后 non_const
将指向 const
数据。
因此那是行不通的。
我的结论是,加上另一个 const
就会正确:
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}
当我现在将 non_const
作为第二个参数传递时,该函数无法
将 in[0]
更改为任何内容,因为该指针现在是不可变的。这有
GCC 6.3 为我提供了很好的服务。现在使用 Intel C++ 17 投入生产,它
不再工作了。
最小的工作示例如下:
#include <cstdint>
typedef float Spinor[3][4][2][8];
template <uint8_t num_flav>
class Solver {
public:
void copy_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}
};
int main(int argc, char **argv) {
Spinor *s1 = new Spinor[10];
Spinor *s2 = new Spinor[10];
Spinor *s1_a[1] = {s1};
Spinor *s2_a[1] = {s2};
Solver<1> s;
s.copy_spinor(s2_a, s1_a);
}
在 GCC 上,它显然解析为第二个 copy_spinor
重载。这
变量 s1_a
扮演前一个 non_const
的角色,允许作为
一个论点。
英特尔 C++ 17 的问题
然而,Intel C++ 17 不接受:
$ icpc -Wall -pedantic const-spinor-const.cpp --std=c++11
const-spinor-const.cpp(23): error: no instance of overloaded function "Solver<num_flav>::copy_spinor [with num_flav=(uint8_t={unsigned char})'[=14=]1']" matches the argument list
argument types are: (Spinor *[1], Spinor *[1])
object type is: Solver<(uint8_t)'[=14=]1'>
s.copy_spinor(s2_a, s1_a);
^
const-spinor-const.cpp(11): note: this candidate was rejected because arguments do not match
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}
^
const-spinor-const.cpp(10): note: this candidate was rejected because arguments do not match
void copy_spinor(Spinor *out, const Spinor *in) {}
^
错误消息不是特别有用,因为它没有说明什么
不允许转换。似乎 const
就是问题所在。
英特尔 C++ 是否有我想念的地方?它是否缺少功能或
我使用了非官方的 GCC 扩展吗?这是英特尔 C++ 中的错误还是
海湾合作委员会?
更新:该示例还使用当前的 Clang 进行编译。
非模板Class
当 class Solver
不是模板 class 时,同样的问题仍然存在。
由于 T a[2]
与函数参数中的 T a[2]
和 T *a
相同,我可以
也可以这样写函数,不需要 uint8_t num_flav
:
void copy_spinor(Spinor *out[], const Spinor *const in[]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}
还是一样的错误
免费功能
同样的问题也发生在非成员非好友非模板函数上:
void free_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}
void free_spinor(Spinor *out[], const Spinor *const in[]) {
std::cout << "Fwd: ";
free_spinor(out[0], in[0]);
}
报错信息是一样的:
$ icpc -Wall -pedantic const-spinor-const.cpp --std=c++11
const-spinor-const.cpp(97): error: no instance of overloaded function "free_spinor" matches the argument list
argument types are: (Spinor *[1], Spinor *[1])
free_spinor(s2_a, s1_a);
^
const-spinor-const.cpp(30): note: this candidate was rejected because arguments do not match
void free_spinor(Spinor *out[], const Spinor *const in[]) {
^
const-spinor-const.cpp(26): note: this candidate was rejected because arguments do not match
void free_spinor(Spinor *out, const Spinor *in) {
^
解决方案尝试
为了运行生产中的代码,我看到了以下选项。 None 的
他们特别有吸引力。
前进的好方法是什么?我可以改变我的新功能
想要,但我想尽可能避免接触调用者代码。
常量包装器
当我在 main
函数中更改 s1_a
的定义以具有两个
const
,它编译:
const Spinor *const s1_a[1] = {s1};
然后使用正确的参数类型调用函数 copy_spinor
。
通用代码的每个用户都必须编写这些 const
每个函数调用的包装器。那会变得很乱。
删除常量正确性。
我可以从函数参数参数中删除最左边的 const
。那
在两个编译器上都能干净地编译。但是,我确实想证明我是
不更改该数组中的任何内容,因此其值应为 const
.
部分解决方案是使用一些预处理器常量来删除
const
仅适用于英特尔编译器。
#ifdef __INTEL_COMPILER
#define ICPC_CONST
#else
#define ICPC_CONST const
#endif
也许用户将一些旋量定义为常量。然后我被卡住了,需要
在那里设置常量:
const Spinor *s3_a[1] = {s3};
s.copy_spinor(s2_a, s3_a);
添加 const
应该比删除它更容易,所以这个解决方案是
很缺乏。此外,上游作者可能会拒绝我的更改,因为
他的代码中的更改。
为每个函数添加一个非常量重载
可以为每个函数添加重载。我有两种变体
通用功能,第二个功能在我使用时启用
英特尔编译器:
void copy_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}
#ifdef __INTEL_COMPILER
void copy_spinor(Spinor *out[num_flav], Spinor *const in[num_flav]) {
std::cout << "Fwd2: ";
copy_spinor(out[0], in[0]);
}
#endif
效果很好,只是有些代码重复。自从我添加
函数只是重用现有的函数,它不是 那么多 代码
复制。还是违反了DRY原则。
另一个缺点是重载的次数是2^N
,其中N是
const *
个参数的数量。有函数占用
像这样的三个参数,因此我需要八份。
让模板推导出常量
使用模板可以抽象出 const Spinor
和 Spinor
。我
可以将函数编写为模板,这样 S
可以是任一数据类型。
使用 static_assert
会给出信息量稍大的错误消息。
template <typename S>
void copy_spinor(Spinor *out[num_flav], S *const in[num_flav]) {
static_assert(std::is_same<Spinor, S>::value ||
std::is_same<const Spinor, S>::value,
"Template parameter must be `const Spinor` or `Spinor`.");
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}
理想情况下,我想指定 S
只能是 Spinor
或 const
Spinor
。也许这对于 C++14 及更高版本是可能的,我必须坚持
C++11.
这个解决方案看起来很干净,我可以添加一个模板参数和一个断言
对于函数中的每个参数。这将很好地扩展并且没有
代码重复。唯一的缺点可能是更长的编译时间(已经
相当长,但不是很重要)和不太有用的错误消息
(希望覆盖static_assert
)。
使用 int **
调用时的错误消息对于 GCC 如下:
const-spinor-const.cpp: In instantiation of 'void Solver<num_flav>::t_copy_spinor(float (**)[3][4][2][8], S* const*) [with S = int; unsigned char num_flav = 1u; Spinor = float [3][4][2][8]]':
const-spinor-const.cpp:86:36: required from here
const-spinor-const.cpp:40:9: error: static assertion failed: Template parameter must be `const Spinor` or `Spinor`.
static_assert(std::is_same<Spinor, S>::value ||
^~~~~~~~~~~~~
const-spinor-const.cpp:45:20: error: no matching function for call to 'Solver<1u>::copy_spinor(float (*&)[3][4][2][8], int* const&)'
copy_spinor(out[0], in[0]);
~~~~~~~~~~~^~~~~~~~~~~~~~~
const-spinor-const.cpp:29:10: note: candidate: void Solver<num_flav>::copy_spinor(float (*)[3][4][2][8], const float (*)[3][4][2][8]) [with unsigned char num_flav = 1u; Spinor = float [3][4][2][8]]
void copy_spinor(Spinor *out, const Spinor *in) {
^~~~~~~~~~~
const-spinor-const.cpp:29:10: note: no known conversion for argument 2 from 'int* const' to 'const float (*)[3][4][2][8]'
const-spinor-const.cpp:33:10: note: candidate: void Solver<num_flav>::copy_spinor(float (**)[3][4][2][8], const float (* const*)[3][4][2][8]) [with unsigned char num_flav = 1u; Spinor = float [3][4][2][8]]
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
^~~~~~~~~~~
const-spinor-const.cpp:33:10: note: no known conversion for argument 1 from 'float (*)[3][4][2][8]' to 'float (**)[3][4][2][8]'
在评论中指出使用enable_if
。这样,我的功能
如下所示:
template <typename S>
typename std::enable_if<std::is_same<const Spinor, const S>::value,
void>::type
t2_copy_spinor(Spinor *out[num_flav], S *const in[num_flav]) {
std::cout << "Fwd: " << typeid(S).name() << " " << typeName<S>() << " ";
copy_spinor(out[0], in[0]);
}
这个更短,也许更简洁。错误信息不包含
不过,不再是我手写的信息了。至少不会出现错误
在 函数中 copy_spinor
但在调用站点,所以用户知道
哪里出了问题。那也许更好。 enable_if
有点
自我解释,至少对更有经验的模板用户。
const-spinor-const.cpp: In function 'int main(int, char**)':
const-spinor-const.cpp:86:37: error: no matching function for call to 'Solver<1u>::t2_copy_spinor(float (* [1])[3][4][2][8], int* [2])'
s.t2_copy_spinor(s2_a, int_array);
^
const-spinor-const.cpp:51:5: note: candidate: template<class S> typename std::enable_if<std::is_same<const float [3][4][2][8], const S>::value, void>::type Solver<num_flav>::t2_copy_spinor(float (**)[3][4][2][8], S* const*) [with S = S; unsigned char num_flav = 1u]
t2_copy_spinor(Spinor *out[num_flav], S *const in[num_flav]) {
^~~~~~~~~~~~~~
const-spinor-const.cpp:51:5: note: template argument deduction/substitution failed:
const-spinor-const.cpp: In substitution of 'template<class S> typename std::enable_if<std::is_same<const float [3][4][2][8], const S>::value, void>::type Solver<num_flav>::t2_copy_spinor(float (**)[3][4][2][8], S* const*) [with S = int]':
const-spinor-const.cpp:86:37: required from here
const-spinor-const.cpp:51:5: error: no type named 'type' in 'struct std::enable_if<false, void>'
enable_if
解决方案看起来比 static_assert
变体更好。
这里是GCC和clang,Intel C++是错的
标准的相关部分是 资格转换 [conv.qual]
(部分编号可以是 4.4 或 4.5)。 C++11 和 C++1z 之间的措辞发生了变化(成为 C++17)...但是所有版本都允许在从最浅层开始的多个级别添加 const
的代码(C ++03, 11, 14, 1z).
需要注意的一个变化是,多级 const 规则现在适用于指针数组,而以前它仅适用于多个指针。但我们实际上是在处理一个多指针,因为在参数声明中找到的数组语法具有指针语义。
不过,也许值得一试
void copy_spinor(Spinor **out, const Spinor *const *in)
以防编译器在应用规则之前混淆/无法将数组类型调整为函数参数列表中的指针类型。
问题
现有代码的扩展
我有一个数字库,它的设计考虑了一种“风味”。 现在我想概括一下。基本数据结构是一个“旋量”,它 本身是一个多维矩阵。有很多功能需要 这些旋量的数组。广义函数需要采用一个这样的旋量 每种口味的数组。
假设有一个函数,它至少执行以下操作:
void copy_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}
我现在的概括是这样的:
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}
在真正的代码中,有一个遍历所有num_flav
的循环,但那不是真的
此处演示需要。
据我了解,必须将此解读为 const Spinor *(in[num_flav])
,
所以 in
是指向可能 num_flav
元素(或另一个
数量,因为 foo[]
只是函数参数中的 *foo
)类型
指向常量旋量的指针。
问题是使用 Spinor *non_const[2]
时无法编译
(没有 const
),请参阅 我之前的
问题。从那里的答案我
已经了解到这不能编译,因为在函数内
copy_spinor
,指针non_const[0]
可以指向一些
const
数组 Spinor *
。然后 non_const
将指向 const
数据。
因此那是行不通的。
我的结论是,加上另一个 const
就会正确:
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}
当我现在将 non_const
作为第二个参数传递时,该函数无法
将 in[0]
更改为任何内容,因为该指针现在是不可变的。这有
GCC 6.3 为我提供了很好的服务。现在使用 Intel C++ 17 投入生产,它
不再工作了。
最小的工作示例如下:
#include <cstdint>
typedef float Spinor[3][4][2][8];
template <uint8_t num_flav>
class Solver {
public:
void copy_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}
};
int main(int argc, char **argv) {
Spinor *s1 = new Spinor[10];
Spinor *s2 = new Spinor[10];
Spinor *s1_a[1] = {s1};
Spinor *s2_a[1] = {s2};
Solver<1> s;
s.copy_spinor(s2_a, s1_a);
}
在 GCC 上,它显然解析为第二个 copy_spinor
重载。这
变量 s1_a
扮演前一个 non_const
的角色,允许作为
一个论点。
英特尔 C++ 17 的问题
然而,Intel C++ 17 不接受:
$ icpc -Wall -pedantic const-spinor-const.cpp --std=c++11
const-spinor-const.cpp(23): error: no instance of overloaded function "Solver<num_flav>::copy_spinor [with num_flav=(uint8_t={unsigned char})'[=14=]1']" matches the argument list
argument types are: (Spinor *[1], Spinor *[1])
object type is: Solver<(uint8_t)'[=14=]1'>
s.copy_spinor(s2_a, s1_a);
^
const-spinor-const.cpp(11): note: this candidate was rejected because arguments do not match
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}
^
const-spinor-const.cpp(10): note: this candidate was rejected because arguments do not match
void copy_spinor(Spinor *out, const Spinor *in) {}
^
错误消息不是特别有用,因为它没有说明什么
不允许转换。似乎 const
就是问题所在。
英特尔 C++ 是否有我想念的地方?它是否缺少功能或 我使用了非官方的 GCC 扩展吗?这是英特尔 C++ 中的错误还是 海湾合作委员会?
更新:该示例还使用当前的 Clang 进行编译。
非模板Class
当 class Solver
不是模板 class 时,同样的问题仍然存在。
由于 T a[2]
与函数参数中的 T a[2]
和 T *a
相同,我可以
也可以这样写函数,不需要 uint8_t num_flav
:
void copy_spinor(Spinor *out[], const Spinor *const in[]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}
还是一样的错误
免费功能
同样的问题也发生在非成员非好友非模板函数上:
void free_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}
void free_spinor(Spinor *out[], const Spinor *const in[]) {
std::cout << "Fwd: ";
free_spinor(out[0], in[0]);
}
报错信息是一样的:
$ icpc -Wall -pedantic const-spinor-const.cpp --std=c++11
const-spinor-const.cpp(97): error: no instance of overloaded function "free_spinor" matches the argument list
argument types are: (Spinor *[1], Spinor *[1])
free_spinor(s2_a, s1_a);
^
const-spinor-const.cpp(30): note: this candidate was rejected because arguments do not match
void free_spinor(Spinor *out[], const Spinor *const in[]) {
^
const-spinor-const.cpp(26): note: this candidate was rejected because arguments do not match
void free_spinor(Spinor *out, const Spinor *in) {
^
解决方案尝试
为了运行生产中的代码,我看到了以下选项。 None 的 他们特别有吸引力。
前进的好方法是什么?我可以改变我的新功能 想要,但我想尽可能避免接触调用者代码。
常量包装器
当我在 main
函数中更改 s1_a
的定义以具有两个
const
,它编译:
const Spinor *const s1_a[1] = {s1};
然后使用正确的参数类型调用函数 copy_spinor
。
通用代码的每个用户都必须编写这些 const
每个函数调用的包装器。那会变得很乱。
删除常量正确性。
我可以从函数参数参数中删除最左边的 const
。那
在两个编译器上都能干净地编译。但是,我确实想证明我是
不更改该数组中的任何内容,因此其值应为 const
.
部分解决方案是使用一些预处理器常量来删除
const
仅适用于英特尔编译器。
#ifdef __INTEL_COMPILER
#define ICPC_CONST
#else
#define ICPC_CONST const
#endif
也许用户将一些旋量定义为常量。然后我被卡住了,需要 在那里设置常量:
const Spinor *s3_a[1] = {s3};
s.copy_spinor(s2_a, s3_a);
添加 const
应该比删除它更容易,所以这个解决方案是
很缺乏。此外,上游作者可能会拒绝我的更改,因为
他的代码中的更改。
为每个函数添加一个非常量重载
可以为每个函数添加重载。我有两种变体 通用功能,第二个功能在我使用时启用 英特尔编译器:
void copy_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}
#ifdef __INTEL_COMPILER
void copy_spinor(Spinor *out[num_flav], Spinor *const in[num_flav]) {
std::cout << "Fwd2: ";
copy_spinor(out[0], in[0]);
}
#endif
效果很好,只是有些代码重复。自从我添加 函数只是重用现有的函数,它不是 那么多 代码 复制。还是违反了DRY原则。
另一个缺点是重载的次数是2^N
,其中N是
const *
个参数的数量。有函数占用
像这样的三个参数,因此我需要八份。
让模板推导出常量
使用模板可以抽象出 const Spinor
和 Spinor
。我
可以将函数编写为模板,这样 S
可以是任一数据类型。
使用 static_assert
会给出信息量稍大的错误消息。
template <typename S>
void copy_spinor(Spinor *out[num_flav], S *const in[num_flav]) {
static_assert(std::is_same<Spinor, S>::value ||
std::is_same<const Spinor, S>::value,
"Template parameter must be `const Spinor` or `Spinor`.");
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}
理想情况下,我想指定 S
只能是 Spinor
或 const
Spinor
。也许这对于 C++14 及更高版本是可能的,我必须坚持
C++11.
这个解决方案看起来很干净,我可以添加一个模板参数和一个断言
对于函数中的每个参数。这将很好地扩展并且没有
代码重复。唯一的缺点可能是更长的编译时间(已经
相当长,但不是很重要)和不太有用的错误消息
(希望覆盖static_assert
)。
使用 int **
调用时的错误消息对于 GCC 如下:
const-spinor-const.cpp: In instantiation of 'void Solver<num_flav>::t_copy_spinor(float (**)[3][4][2][8], S* const*) [with S = int; unsigned char num_flav = 1u; Spinor = float [3][4][2][8]]':
const-spinor-const.cpp:86:36: required from here
const-spinor-const.cpp:40:9: error: static assertion failed: Template parameter must be `const Spinor` or `Spinor`.
static_assert(std::is_same<Spinor, S>::value ||
^~~~~~~~~~~~~
const-spinor-const.cpp:45:20: error: no matching function for call to 'Solver<1u>::copy_spinor(float (*&)[3][4][2][8], int* const&)'
copy_spinor(out[0], in[0]);
~~~~~~~~~~~^~~~~~~~~~~~~~~
const-spinor-const.cpp:29:10: note: candidate: void Solver<num_flav>::copy_spinor(float (*)[3][4][2][8], const float (*)[3][4][2][8]) [with unsigned char num_flav = 1u; Spinor = float [3][4][2][8]]
void copy_spinor(Spinor *out, const Spinor *in) {
^~~~~~~~~~~
const-spinor-const.cpp:29:10: note: no known conversion for argument 2 from 'int* const' to 'const float (*)[3][4][2][8]'
const-spinor-const.cpp:33:10: note: candidate: void Solver<num_flav>::copy_spinor(float (**)[3][4][2][8], const float (* const*)[3][4][2][8]) [with unsigned char num_flav = 1u; Spinor = float [3][4][2][8]]
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
^~~~~~~~~~~
const-spinor-const.cpp:33:10: note: no known conversion for argument 1 from 'float (*)[3][4][2][8]' to 'float (**)[3][4][2][8]'
在评论中指出使用enable_if
。这样,我的功能
如下所示:
template <typename S>
typename std::enable_if<std::is_same<const Spinor, const S>::value,
void>::type
t2_copy_spinor(Spinor *out[num_flav], S *const in[num_flav]) {
std::cout << "Fwd: " << typeid(S).name() << " " << typeName<S>() << " ";
copy_spinor(out[0], in[0]);
}
这个更短,也许更简洁。错误信息不包含
不过,不再是我手写的信息了。至少不会出现错误
在 函数中 copy_spinor
但在调用站点,所以用户知道
哪里出了问题。那也许更好。 enable_if
有点
自我解释,至少对更有经验的模板用户。
const-spinor-const.cpp: In function 'int main(int, char**)':
const-spinor-const.cpp:86:37: error: no matching function for call to 'Solver<1u>::t2_copy_spinor(float (* [1])[3][4][2][8], int* [2])'
s.t2_copy_spinor(s2_a, int_array);
^
const-spinor-const.cpp:51:5: note: candidate: template<class S> typename std::enable_if<std::is_same<const float [3][4][2][8], const S>::value, void>::type Solver<num_flav>::t2_copy_spinor(float (**)[3][4][2][8], S* const*) [with S = S; unsigned char num_flav = 1u]
t2_copy_spinor(Spinor *out[num_flav], S *const in[num_flav]) {
^~~~~~~~~~~~~~
const-spinor-const.cpp:51:5: note: template argument deduction/substitution failed:
const-spinor-const.cpp: In substitution of 'template<class S> typename std::enable_if<std::is_same<const float [3][4][2][8], const S>::value, void>::type Solver<num_flav>::t2_copy_spinor(float (**)[3][4][2][8], S* const*) [with S = int]':
const-spinor-const.cpp:86:37: required from here
const-spinor-const.cpp:51:5: error: no type named 'type' in 'struct std::enable_if<false, void>'
enable_if
解决方案看起来比 static_assert
变体更好。
这里是GCC和clang,Intel C++是错的
标准的相关部分是 资格转换 [conv.qual]
(部分编号可以是 4.4 或 4.5)。 C++11 和 C++1z 之间的措辞发生了变化(成为 C++17)...但是所有版本都允许在从最浅层开始的多个级别添加 const
的代码(C ++03, 11, 14, 1z).
需要注意的一个变化是,多级 const 规则现在适用于指针数组,而以前它仅适用于多个指针。但我们实际上是在处理一个多指针,因为在参数声明中找到的数组语法具有指针语义。
不过,也许值得一试
void copy_spinor(Spinor **out, const Spinor *const *in)
以防编译器在应用规则之前混淆/无法将数组类型调整为函数参数列表中的指针类型。